package com.ywk.base.sign;

import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.digest.MD5;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Map;

/**
 * 签名
 * RSA非对称加密：公钥加密私钥解，私钥签名公钥验
 * @Author zbh
 * @Date 2023/5/5
 */
@RestController
@RequestMapping("/sign")
public class SignDemo {

    private static String PUBLIC_KEY="MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANii54ALlJ8bmJjfA+FDMZQWJAupmPUN" +
            "A0KJwbrFPG506pSYkJ0q8koIbie12J5MVUCFs0SgQTwbo5E7DZsXLI8CAwEAAQ==";

    private static String PRIVATE_KEY="MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEA2KLngAuUnxuYmN8D" +
            "4UMxlBYkC6mY9Q0DQonBusU8bnTqlJiQnSrySghuJ7XYnkxVQIWzRKBBPBujkTsN" +
            "mxcsjwIDAQABAkBu7YuI32Usy56puJhuAlAiueufJSqzYARpT8ypbFFzZO04d6dW" +
            "ybzO6l3ZKr3/CAMpYGtPl1s7EbZYYJusalsRAiEA93tNnqtXrOWug0XMLR43bKKo" +
            "6P73QjbDNd/2RMnbvT0CIQDgF85fLr0qoD3uFwyJ93/cJBU8hIejQtMZvzDzIgTF" +
            "uwIhAKRZLCjM3MKjHQtsVLU5tdDdDJL+jzFXsLecba0s6FJ5AiEA2NSQHi9GYJle" +
            "cyMgQdsWf7b2SXFfIQzG4FHMFmWjQoMCIE5XwQHaS34y38NZIHkrjXINgiXYezWF" +
            "F69YLckrbFph";

    private static RSA rsa= new RSA(PRIVATE_KEY,PUBLIC_KEY);

    public static void main(String[] args) {
        String sign = rsa.encryptBase64("aaa=123", KeyType.PublicKey);
        String decryptStr = rsa.decryptStr(sign, KeyType.PrivateKey);
        System.out.println(decryptStr);
    }

    /**
     * 校验签名（功能：签名具有失效性，粒度细化到接口，所以需要对入参进行校验）
     * 签名 sign = rsa加密（MD5加密入参&时间戳）
     * 校验逻辑：
     * 1.接口时效性校验
     * 2.需要对接口参数进行校验
     * @param param
     * @return
     */
    @RequestMapping("/signCheck")
    public String signCheck(@RequestBody Map<String,String> param){
        //1.获取签名
        String sign = param.remove("sign");
        if (StringUtils.isEmpty(sign)) {
            return "fail,sign null";
        }
        //2.解密签名：MD5加密参数&time
        String decryptStr = rsa.decryptStr(sign, KeyType.PrivateKey);
        String[] split = decryptStr.split("&");
        //3.接口时效性校验：20秒内有效
        if (System.currentTimeMillis()-Long.parseLong(split[0])>20000) {
            return "fail,timeout";
        }
        //4.需要对接口参数进行校验
        ArrayList<String> paramList = new ArrayList<>(param.size());
        for (Map.Entry<String, String> entry : param.entrySet()) {
            paramList.add(entry.getKey()+"="+entry.getValue());
        }
        //参数排序，以防入参相同但是顺序不同导致签名前后不同
        paramList.sort(String::compareTo);
        String paramStr = String.join(",", paramList);
        //加密参数
        String realSign = MD5.create().digestHex(paramStr);
        //与签名加密参数对比校验
        if (!realSign.equals(split[1])) {
            return "fail";
        }
        return "pass";
    }

    /**
     * 获取签名（功能：签名具有失效性，粒度细化到接口，所以需要对入参进行校验）
     * 签名 sign = rsa加密（MD5加密入参&时间戳）
     * @param param
     * @return
     */
    @RequestMapping("/getSign")
    public String getSign(@RequestBody Map<String,String> param){
        ArrayList<String> paramList = new ArrayList<>(param.size());
        for (Map.Entry<String, String> entry : param.entrySet()) {
            paramList.add(entry.getKey()+"="+entry.getValue());
        }
        paramList.sort(String::compareTo);
        String paramStr = String.join(",", paramList);
        //加密参数
        String s = MD5.create().digestHex(paramStr);
        //rsa加密（入参&时间戳）获取签名 sign
        String sign = rsa.encryptBase64(System.currentTimeMillis()+ "&" + s , KeyType.PublicKey);
        return sign;
    }
}
